#region Using directives

using System;
using System.Collections.Generic;

#endregion

namespace Weazel.Gezel.Model 
{
  public partial class Datapath : Block
  {
    /// <summary>
    /// The Datapath.Net class represents a connection from an 
    /// outport or external stimuli to one or more
    /// inports.
    /// </summary>
    public class Net : AbstractEntity<Datapath>
    {
      /// <summary>
      /// The output port which drives the
      /// inport
      /// </summary>
      private CommonEntity outport;
      public CommonEntity OutPort
      {
        get
        {
          return outport;
        }
        set
        {
          outport = value;
        }
      }

      /// <summary>
      /// The inport which are connected by this net.
      /// </summary>
      private CommonEntity inport;
      public CommonEntity InPort
      {
        get
        {
          return inport;
        }
        set
        {
          inport = value;
        }
      }

      /// <summary>
      /// Constructs a new system net with the 
      /// specified local name and parent.
      /// </summary>
      /// <param name="parent">containing system</param>
      /// <param name="name">local name</param>
      public Net(Datapath parent, string name) : base(parent, name)
      {
      }
    }

    /// <summary>
    /// The controller which governs execution of
    /// this datapath. This reference is null until
    /// model verification has been completed
    /// </summary>
    protected Controller controller;

    /// <summary>
    /// Maps from the signal names to the Signal
    /// instances contained in this datapath
    /// </summary>
    private readonly Dictionary<string, Signal> signals;
    public Dictionary<string, Signal> Signals
    {
      get
      {
        return signals;
      }
    }

    /// <summary>
    /// The list of tracer blocks contained in this datapath
    /// </summary>
    private readonly List<SignalTrace> tracers;
    public List<SignalTrace> Tracers
    {
      get
      {
        return tracers;
      }
    }

    /// <summary>
    /// Datapaths used in this datapath
    /// </summary>
    private readonly Dictionary<string, Datapath> children;

		private Dictionary<string, IPBlock> childIpBlocks;
		public Dictionary<string, IPBlock> ChildIpBlocks
		{
			get { return childIpBlocks; }
		} 

    /// <summary>
    /// The list of datapath nets hashed on local name.
    /// </summary>
    private readonly List<Datapath.Net> nets;
    public List<Datapath.Net> Nets
    {
      get
      {
        return nets;
      }
    }

    /// <summary>
    /// Maps from referenced child datapath names to
    /// the list of signal names used to interconnect
    /// the child datapath with its parent
    /// </summary>
    private Dictionary<string, string[]> childDatapathRefs;
    public Dictionary<string, string[]> ChildDatapathRefs
    {
      get
      {
        return childDatapathRefs;
      }
    }

    /// <summary>
    /// Maps from the register names to the Register
    /// instances contained in this datapath
    /// </summary>
    private Dictionary<string, Register> registers;
    public Dictionary<string, Register> Registers
    {
      get
      {
        return registers;
      }
    }

    /// <summary>
    /// Maps from the signalflowgraph names to the SignalFlowGraph
    /// instances contained in this datapath
    /// </summary>
    protected readonly Dictionary<string, SignalFlowGraph> signalFlowGraphs;
    public Dictionary<string, SignalFlowGraph> SignalFlowGraphs
    {
      get
      {
        return signalFlowGraphs;
      }
    }

    protected readonly Dictionary<string, LookupTable> luts;
    public Dictionary<string, LookupTable> LookupTables
    {
      get
      {
        return luts;
      }
    }

    private bool inSystem = false;
    
    /// <summary>
    /// True iff datapath is part of a
    /// system or child of a datapath which
    /// is in a system. Only datapaths which
    /// are in a system will be run during
    /// simulation.
    /// </summary>
    public bool InSystem
    {
      get
      {
        return inSystem;
      }
      internal set
      {
        inSystem = value;
        // remember to also mark contained children 
        foreach (Datapath child in children.Values)
        {
          child.InSystem = value;
        }
      }
    }

    private string clonedFromDatapath;
    public string ClonedFromDatapath
    {
      get
      {
        return clonedFromDatapath;
      }
      set
      {
        clonedFromDatapath = value;
      }
    }

    /// <summary>
    /// Convenience constructor
    /// </summary>
    /// <param name="parent">parent model</param>
    /// <param name="name">local name of datapath</param>
    public Datapath(Model parent, string name) : this((IEntity<CommonEntity>) parent, name)
    {
    }

		public Datapath(
			Model parent, 
			string name, 
			Position startPosition,
			Position endPosition
		)
			: this((IEntity<CommonEntity>)parent, name, startPosition, endPosition)
    {
    }

    /// <summary>
    /// Convenience constructor
    /// </summary>
    /// <param name="parent">parent Datapath</param>
    /// <param name="name">local name of datapath</param>
    public Datapath(Datapath parent, string name) : this((IEntity<CommonEntity>) parent, name)
    {
    }

		public Datapath(
			Datapath parent, 
			string name, 
			Position startPosition,
			Position endPosition
		)
			: this((IEntity<CommonEntity>)parent, name, startPosition, endPosition)
    {
    }

    /// <summary>
    /// Common constructor of the datapath which initializes
    /// its internal datastructures and assignes the specified
    /// parent and local name.
    /// </summary>
    /// <param name="parent">parent model or datapath</param>
    /// <param name="name">local name of datapath</param>
    private Datapath(IEntity<CommonEntity> parent, string name) : base(parent, name)
    {
      this.clonedFromDatapath = null;
      this.signals = new Dictionary<string, Signal>();
      this.registers = new Dictionary<string, Register>();
      
      this.luts = new Dictionary<string, LookupTable>();
      this.signalFlowGraphs = new Dictionary<string, SignalFlowGraph>();
      this.children = new Dictionary<string, Datapath>();
      this.childDatapathRefs = new Dictionary<string, string[]>();
      this.nets = new List<Datapath.Net>();
      this.tracers = new List<SignalTrace>();
			this.childIpBlocks = new Dictionary<string, IPBlock>();
    }

		private Datapath(
			IEntity<CommonEntity> parent, 
			string name,
			Position startPosition,
			Position endPosition
		)
			: base(parent, name, startPosition, endPosition)
    {
      this.clonedFromDatapath = null;
      this.signals = new Dictionary<string, Signal>();
      this.registers = new Dictionary<string, Register>();

      this.luts = new Dictionary<string, LookupTable>();
      this.signalFlowGraphs = new Dictionary<string, SignalFlowGraph>();
      this.children = new Dictionary<string, Datapath>();
      this.childDatapathRefs = new Dictionary<string, string[]>();
      this.nets = new List<Datapath.Net>();
      this.tracers = new List<SignalTrace>();
			this.childIpBlocks = new Dictionary<string, IPBlock>();
    }

    /// <summary>
    /// Looks up the entity with the given local name
    /// in the scope of this datapath instance
    /// </summary>
    /// <param name="identifier">name of the entity</param>
    /// <returns>referenced entity</returns>
    public CommonEntity GetIdentifier(string identifier)
    {
      // model object contains all constants .. must not be null
      System.Diagnostics.Debug.Assert(Model != null);

      if (inPorts.ContainsKey(identifier))
        return inPorts[identifier];
      else if (outPorts.ContainsKey(identifier))
        return outPorts[identifier];
      else if (registers.ContainsKey(identifier))
        return registers[identifier];
      else if (signals.ContainsKey(identifier))
        return signals[identifier];
      else if (luts.ContainsKey(identifier))
        return luts[identifier];
      else if (Model.Constants.ContainsKey(identifier))
        return Model.Constants[identifier];
      else
      {
        foreach (SignalFlowGraph sfg in SignalFlowGraphs.Values)
          if (sfg.Signal != null && sfg.Signal.LocalName == identifier)
            return sfg.Signal;
      }
      return null;
    }

    /// <summary>
    /// Looks up the id of the entity with the given local name
    /// in the scope of this datapath instance
    /// </summary>
    /// <param name="identifier">name of the entity</param>
    /// <returns>id of referenced entity</returns>
    public int GetIdentifierId(string identifier)
    {
      if (inPorts.ContainsKey(identifier))
        return inPorts[identifier].Id;
      else if (outPorts.ContainsKey(identifier))
        return outPorts[identifier].Id;
      else if (registers.ContainsKey(identifier))
        return registers[identifier].Id;
      else if (signals.ContainsKey(identifier))
        return signals[identifier].Id;
      else if (luts.ContainsKey(identifier))
        return luts[identifier].Id;
      throw new ApplicationException("internal error, identifier is not in datapath");
    }

    public void Expand()
    {
      foreach (string childDatapathName in this.childDatapathRefs.Keys)
      {
        Datapath datapath = Model.Datapaths[childDatapathName];

        string childName = 
          Name + "." + childDatapathName;

        Datapath cloned = datapath.Clone(childName);

        Model.Datapaths.Add(childName, cloned);
      }
    }


    /// <summary>
    /// Performs a deep copy of this datapath and
    /// its contents. Lookuptables however are not 
    /// duplicated but copied by reference.
    /// </summary>
    /// <param name="cloneName">name of the cloned datapath</param>
    /// <returns>the datapath clone</returns>
    public virtual Datapath Clone(string cloneName)
    {
      // note: clone has same source file position as parent
      Datapath cloned = new Datapath((IEntity<CommonEntity>)this.parent, cloneName, startPosition, endPosition);

      Console.WriteLine("Creating: " + cloneName);

      // note: copy by reference (consider revision)
      cloned.childDatapathRefs = childDatapathRefs;
      cloned.childIpBlocks = childIpBlocks;

      foreach (string inportName in inPorts.Keys)
        cloned.InPorts[inportName] = inPorts[inportName].Clone(cloned);
      
      foreach (string outportName in outPorts.Keys)
        cloned.outPorts[outportName] = outPorts[outportName].Clone(cloned);

      foreach (string registerName in registers.Keys)
        cloned.registers[registerName] = registers[registerName].Clone(cloned);

      foreach (string sfgName in signalFlowGraphs.Keys)
        cloned.signalFlowGraphs[sfgName] = signalFlowGraphs[sfgName].Clone(cloned);

      foreach (string signalName in signals.Keys)
        cloned.signals[signalName] = signals[signalName].Clone(cloned);

      foreach (string lutName in luts.Keys)
        cloned.luts[lutName] = luts[lutName]; // just copy reference

      return cloned;
    }
  }
}
